    // bugTest.cpp: 定义应用程序的入口点。
//

// 总结：
// 1.第一个子类的主要问题: 调用大小写函数时返回的是一个临时变量，并且让char*去指向这个临时变量
//   当调用语句结束后，临时变量死掉，内存归还给操作系统，属于被释放的区域，但此时test1指针仍然指向这块内存

// 2.strcpy用的不好，每次传入的是8个字母加一个系统自动填充的'\0'，而buffer缓冲区的大小又不够
//   strcpy会一直拷贝给buffer直到遇到'\0'，但此时已经拷贝到溢出了，就是说'\0'拷贝到不属于buffer的区域了

// 3.vector的erase会导致迭代器失效的问题，其底层的迭代器就是一个被封装的指针，使用时可以看作下标来使用
//   如果vector的内容发生变化，那原来下标指向的内容也会跟着变化，实际上就是被删除往后的数据都往前挪一段位置
//   那么就不需要对it++

#include <iostream>
#include <string>

#include <algorithm>
#include <vector>

// 下面第三个子类用到的 strcpy 是<string.h>里的，个人习惯用cstring代替（支持C/C++混编）
#include <cstring>

#include <cstdlib>

class BugTemplate 
{
public:
    virtual void FixTest() = 0;
};

//存在1个bug
class ClassBug :public BugTemplate
{
private:
    // 大写
    const std::string StringToUpper(const char* src)
    {
        std::string temp;
        temp = src;
        std::transform(temp.begin(), temp.end(), temp.begin(), ::toupper);
        // std::cout << "出函数前: 测试StringToUpper-大写: " << temp.c_str() << std::endl;
        return temp;
    }

    // 小写
    const std::string StringToLower(const char* src)
    {
        // std::cout << std::endl << "进入小写函数StringToLower后,看看src是多少" << src << std::endl;
        // 输出结果: 进入小写函数StringToLower后,看看src是多少ABCddasdasdaddaweqWADfFcddasdasdaddaweqewabcddasdasdaddaweWQD
        std::string temp;
        temp = src;
        
        std::transform(temp.begin(), temp.end(), temp.begin(), ::tolower);
        // std::cout << "出函数前: 测试StringToLower-小写: " << temp.c_str() << std::endl;

        return temp;
    }

public:
    ClassBug() {}
    ~ClassBug() {}
    //需要修复乱码问题
    //tip:考察对类的作用域理解
    virtual void FixTest()
    {       
            std::cout << "请修复bug，使程序正确输出转大写转小写" << std::endl;
            const char* test = "ABCddasdasdaddaweqWADfFcddasdasdaddaweqewabcddasdasdaddaweWQD";

            // 计算test的大小，用string存起来后再调用对象里的size方法
            std::string temp = test;
            size_t size = temp.size();

            // const char* test1 = StringToUpper(test).c_str();
            char test1[size];
            memcpy(test1, StringToUpper(test).c_str(),size);
            
            // printf("函数调用后:  test1的地址: %p\n", test1);
            // std::cout << "出函数后: 测试StringToUpper-大写: test1 :" << test1 << std::endl;

            // const char* test2 = StringToLower(test).c_str();
            // std::cout << "出函数后: 测试StringToLower-小写: test2 " << test2 << std::endl;
            // printf("函数调用后:  test2的地址: %p\n", test2);

            char test2[size];
            memcpy(test2, StringToLower(test).c_str(),size);

            // std::cout << "test2之后 再次 测试StringToUpper-大写: test1 :" << test1 << std::endl;

            // std::cout << "===============================================================" << std::endl;

            // 打印出来的结果是
            // 出函数前: 测试StringToUpper-大写: ABCDDASDASDADDAWEQWADFFCDDASDASDADDAWEQEWABCDDASDASDADDAWEWQD
            // 出函数后: 测试StringToUpper-大写: test1 :ABCDDASDASDADDAWEQWADFFCDDASDASDADDAWEQEWABCDDASDASDADDAWEWQD
            // 出函数前: 测试StringToLower-小写: abcddasdasdaddaweqwadffcddasdasdaddaweqewabcddasdasdaddawewqd
            // 出函数后: 测试StringToLower-小写: test2 abcddasdasdaddaweqwadffcddasdasdaddaweqewabcddasdasdaddawewqd
            // ===============================================================
            // ABCddasdasdaddaweqWADfFcddasdasdaddaweqewabcddasdasdaddaweWQD
            // abcddasdasdaddaweqwadffcddasdasdaddaweqewabcddasdasdaddawewqd
            // abcddasdasdaddaweqwadffcddasdasdaddaweqewabcddasdasdaddawewqd

            // 函数调用后，返回给test1和test2，之后第一时间打印没有问题

            // 增加一个: 打印test2后再次打印test1，查看是否会因为调用小写函数影响到test1
            // 打印结果: test2之后 再次 测试StringToUpper-大写: test1 :abcddasdasdaddaweqwadffcddasdasdaddaweqewabcddasdasdaddawewqd
            // 就锁定问题在小写函数调用后了

            // 第一想法就是，test1的内容被test2或者小写函数内部的tranform给覆盖了，去打印一下地址看看
            // printf("函数调用后:  test1的地址: %p, test2的地址: %p\n", test1, test2);
            // 输出结果: test1的地址: 0x1dc9c38, test2的地址: 0x1dc9c38
            // OK，那问题就是被覆盖，为了谨慎起见再查看一下test的地址，然后在test1和test2被创建出来后的位置再次查看地址

            // printf("test的地址: %p\n",test); // test不受到影响
            // 猜想: test1和test2创建出来后，大小写函数返回一个string类型通的临时变量传给test1和test2指向的区域
            //       但是test1和test2本质上是一个指针，指向的是返回的一个临时变量
            //       当函数被调用完后，临时变量的生命死掉，空间被释放，但是这个时候的test1仍然指向临时变量给他的内存
            //       就是说明test1此时指向一个被释放的空间，这是一种危险行为
            //       猜测后面test2获得的内存地址，就是test1指向的那块被释放的内存，所以才造成了地址相同的问题！！！
            // 个人感觉更像是考察函数返回是如何传给调用它自身的函数，以及内存管理的知识

            // 解决方法: 直接不使用char*去存储数据，返回时用string或者char[]去接收


            std::cout << test << std::endl;
            std::cout << test1 << std::endl;
            std::cout << test2 << std::endl;
        
    }

};

struct buffertest 
{
    // char strbuffer1[8];
    // char strbuffer2[8];
    // char strbuffer3[8];

    // 这里修改是因为CopyBug的strcpy

    // char strbuffer1[9];
    // char strbuffer2[9];
    // char strbuffer3[9];

    // 不想要存'\0'了
    char strbuffer1[8];
    char strbuffer2[8];
    char strbuffer3[8];
};

//存在3个bug
class CopyBug :public BugTemplate
{
public:
    CopyBug() {}
    ~CopyBug() {}
    //需要修复乱码问题
    virtual void FixTest()
    {
        
        std::cout << "请让程序能够正常输出ABCDEFGHIJKLMNOP" << std::endl;
        buffertest* buffer = (buffertest*)malloc(sizeof(buffertest));

        // strcpy(buffer->strbuffer2, "IJKLMNOP");
        // strcpy(buffer->strbuffer1, "ABCDEFGH");

        // std::cout << buffer->strbuffer1;
        // std::cout << buffer->strbuffer2 << std::endl;

        // 这一段输出是：ABCDEFGH 后面的strbuffer2没有打出来
        // 看了一下，buffer里面存的buffer都是8字节，然后每次拷贝"ABCDEFGH"九个字节，字符串后面会补个\0
        // 首先就是buffer缓冲区放不下那么多，strcpy会读到'\0'才结束，溢出的数据会影响到buffer缓冲区外的数据
        // 故将buffer缓冲区增大，多加一个就行，用来保存'\0'
        
        // 后面想到这种方法有个bug，就是buffer的最后一个值是'\0'，联想到：如果用自定义协议，其中协议用到了'\0'
        // 可能会出问题，因此换个思路，尝试在不改变buffer大小的情况下，只把字符串的内容放进去，不放'\0'
        // 用strncpy就得了，指定大小拷贝到buffer里
        strncpy(buffer->strbuffer2, "IJKLMNOP", 8);
        strncpy(buffer->strbuffer1, "ABCDEFGH", 8);
        std::cout << buffer->strbuffer1;
        std::cout << buffer->strbuffer2 << std::endl;
    }
};

//存在一个bug
class VectorBug :public BugTemplate
{
public:
    VectorBug() {}
    ~VectorBug() {}
    virtual void FixTest()
    {
        std::vector<int> vec = {1,2,3,4,5,6,7,8,9};
        std::cout << "正确使用迭代器删除程序前三个数值,使程序不崩溃" << std::endl;
        std::vector<int>::iterator it = vec.begin();
        for (int i = 0; i < 3; i++)
        {
            // 很明显的迭代器失效了，erase函数会让迭代器指向删除后的下一个元素，如果是末尾就是end()的返回值
            vec.erase(it);

            //it++;
            it;     //这里不需要处理，it迭代器底层会指向被删除的下一个元素 
        }
        for (it = vec.begin(); it != vec.end(); it++)
        {
            std::cout << *it << " ";
        }

    }
};


//修复bug 使程序正常运作
int main()
{
    // 解决最后一个bug
    {
        ClassBug bug;
        bug.FixTest();
        // ABCddasdasdaddaweqWADfFcddasdasdaddaweqewabcddasdasdaddaweWQD
        // abcddasdasdaddaweqwadffcddasdasdaddaweqewabcddasdasdaddawewqd
        // abcddasdasdaddaweqwadffcddasdasdaddaweqewabcddasdasdaddawewqd
        // 这是原本程序输出打印的结果，第一反应就是先去查看FixTest里的实现
    }

    // 解决第二个bug
    {
        CopyBug bug;
        bug.FixTest();
    }

    // 这里是解决的第一个bug
    {
        VectorBug bug;
        bug.FixTest();
    }
    system("pause");
	return 0;
}
